import com.tencent.cos.xml.model.CosXmlRequest
import java.net.URLEncoder
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import java.util.*
import kotlin.text.Charsets.UTF_8

object V5Signer {
    fun sign(secretId: String, secretKey: String, request: CosXmlRequest): String {
        val method = request.method.uppercase(Locale.ROOT)
        val path = request.requestURL.replace("^/+".toRegex(), "/")
        val headers = request.requestHeaders ?: mapOf()
        val queries = request.queryString ?: ""

        // 当前时间戳
        val now = System.currentTimeMillis() / 1000
        val signTime = "$now;${now + 600}" // 有效期 10 分钟

        // 1. 生成 sign key
        val signKey = hmacSha1Hex(secretKey, signTime)

        // 2. 处理 URL 参数（按 key 排序）
        val queryMap = parseQueryParams(queries as? String ?: "")
        val paramList = queryMap.keys.sorted().joinToString(";")
        val paramStr = queryMap.entries.sortedBy { it.key }
            .joinToString("&") { "${urlEncode(it.key)}=${urlEncode(it.value)}" }

        // 3. 处理 Headers（只取 host、content-type 等）
        val headerMap = headers.mapKeys { it.key.lowercase(Locale.ROOT) }
        val headerList = headerMap.keys.sorted().joinToString(";")
        val headerStr = headerMap.entries.sortedBy { it.key }
          .joinToString("&") {
              val key = urlEncode(it.key)
              val value = when (val v = it.value) {
                  is List<*> -> urlEncode(v.joinToString(","))  // 多值 header 处理为以逗号连接的字符串
                  else -> urlEncode(v.toString())
              }
              "$key=$value"
          }

        // 4. 构造 format string
        val formatString = "$method\n$path\n$paramStr\n$headerStr\n"
        val formatSha1 = sha1Hex(formatString)

        // 5. 构造 string to sign
        val stringToSign = "sha1\n$signTime\n$formatSha1\n"
        val signature = hmacSha1Hex(signKey, stringToSign)

        // 6. 构造 authorization
        return buildString {
            append("q-sign-algorithm=sha1")
            append("&q-ak=$secretId")
            append("&q-sign-time=$signTime")
            append("&q-key-time=$signTime")
            append("&q-header-list=$headerList")
            append("&q-url-param-list=$paramList")
            append("&q-signature=$signature")
        }
    }

    private fun hmacSha1Hex(key: String, msg: String): String {
        val mac = Mac.getInstance("HmacSHA1")
        mac.init(SecretKeySpec(key.toByteArray(UTF_8), "HmacSHA1"))
        return mac.doFinal(msg.toByteArray(UTF_8)).joinToString("") { "%02x".format(it) }
    }

    private fun sha1Hex(msg: String): String {
        val digest = MessageDigest.getInstance("SHA-1")
        return digest.digest(msg.toByteArray(UTF_8)).joinToString("") { "%02x".format(it) }
    }

    private fun urlEncode(input: String): String {
        return URLEncoder.encode(input, "UTF-8")
            .replace("+", "%20").replace("*", "%2A")
            .replace("%7E", "~").lowercase(Locale.ROOT)
    }

    private fun parseQueryParams(query: String): Map<String, String> {
        val map = mutableMapOf<String, String>()
        if (query.isBlank()) return map
        for (pair in query.split("&")) {
            val parts = pair.split("=", limit = 2)
            if (parts.size == 2) map[parts[0]] = parts[1]
        }
        return map
    }
}
